use std::fmt::{Debug, Pointer, Write};

use rand::prelude::*;

#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
pub enum CardType {
    Tong,
    Tiao,
    Wan,
    None,
}

#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Card(i8);

impl Card {
    pub fn new(t: CardType, num: i8) -> Self {
        match t {
            CardType::Tong => Card(0 + num),
            CardType::Tiao => Card(10 + num),
            CardType::Wan => Card(20 + num),
            CardType::None => Card(0),
        }
    }

    pub fn tong(num: i8) -> Self {
        Card::new(CardType::Tong, num)
    }

    pub fn tiao(num: i8) -> Self {
        Card::new(CardType::Tiao, num)
    }

    pub fn wan(num: i8) -> Self {
        Card::new(CardType::Wan, num)
    }

    pub fn next_n(self, n: i8) -> Self {
        let tp = self.tp();
        let new = Card(self.0 + n);
        if new.tp() != tp && tp == CardType::None {
            return Card::new(CardType::None, 0);
        }

        new
    }

    pub fn tp(self) -> CardType {
        match self.0 {
            1..=9 => CardType::Tong,
            11..=19 => CardType::Tiao,
            21..=29 => CardType::Wan,
            _ => CardType::None,
        }
    }

    pub fn num(self) -> i8 {
        self.0 % 10
    }

    pub fn next(self) -> Self {
        Card(self.0 + 1)
    }
}

impl Debug for Card {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}({})", self.tp(), self.num())
    }
}

pub struct Cards {
    vec: Vec<i8>,
    top: usize,
}

impl Cards {
    pub fn with_size(s: usize) -> Self {
        let vec = Vec::with_capacity(s);
        Cards {
            vec: vec,
            top: s - 1,
        }
    }
    pub fn new_108() -> Self {
        let mut cards = Cards::with_size(108);
        for i in 0..108 {
            cards.vec.push(i)
        }

        cards
    }

    pub fn shuffle(&mut self) {
        let mut rng = thread_rng();
        self.vec.shuffle(&mut rng);
        self.top = 107
    }

    pub fn pop(&mut self) -> Card {
        if self.top < 0 {
            return Card::new(CardType::None, 0);
        }

        let num = self.vec[self.top];
        self.top = self.top - 1;

        return match (num / 36) {
            0 => Card::new(CardType::Tong, num % 9 + 1),
            1 => Card::new(CardType::Tiao, num % 9 + 1),
            2 => Card::new(CardType::Wan, num % 9 + 1),
            _ => Card::new(CardType::None, 0),
        };
    }
}

pub type PlayerId = u32;

#[derive(PartialEq, Eq)]
pub enum PlayerState {
    Sat,
    Ready,
    Wait,
    Deciding,
}

pub enum DecidingType {
    DingQue,
    Da,
    Peng(Card),
    Gang(Card),
    ZiMo(Card),
    Hu(Card),
    Skip,
}

pub struct Player {
    pub id: PlayerId,
    pub state: PlayerState,
    pub que: CardType,
    pub hand_cards: Vec<Card>,
    pub decidings: Vec<DecidingType>,
}

impl Player {
    pub fn with_id(id: PlayerId) -> Self {
        Player {
            id,
            state: PlayerState::Ready,
            que: CardType::None,
            hand_cards: Vec::new(),
            decidings: Vec::new(),
        }
    }
}

#[cfg(test)]
mod test {
    use crate::majhong::element::CardType;

    use super::{Card, Cards};

    #[test]
    fn cards_pop() {
        let mut cards = Cards::new_108();
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 9));
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 8));
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 7));
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 6));
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 5));
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 4));
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 3));
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 2));
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 1));
        assert_eq!(cards.pop(), Card::new(CardType::Wan, 9));
    }

    #[test]
    fn shuffle() {
        let mut cards = Cards::new_108();
        cards.shuffle();
        println!("{:?}", cards.pop());
        println!("{:?}", cards.pop());
        println!("{:?}", cards.pop());
        println!("{:?}", cards.pop());
        println!("{:?}", cards.pop());
        println!("{:?}", cards.pop());
    }
}
